#include<cstdio>
#include<iostream>
#include<stdlib.h>
#include<cstring>
#include<sys/types.h>
#include<unistd.h>
#include<sys/wait.h>
#include<cctype>
#include<sys/stat.h>
#include<fcntl.h>

#define FORMAT "[%s@%s:%s]# "
#define SPARATOR "/"
#define COMMANDMAXSIZE 1024
#define ARGVMAXSIZE  128
#define ENVMAX 100

//重定向
#define NOREDIR 0
#define OUTPUTREDIR 1
#define APPENDREDIR 2
#define INPUTREDIR 3

int flag = 0;
std::string  filename;

char* argv[ARGVMAXSIZE];
int size =0;

//创建环境变量表
char* env[ENVMAX];
int envs = 0;
int lastcode = 0;
//
char cwd [1024];
//
const char* GetUserName()
{
    const char* username = getenv("USER");
    if(username == NULL)
    {
        return "None";
    }
     
    return username;
}

const char* GetPwd()
{
//    const char* pwd =  getenv("PWD");
//    if(pwd == NULL)
//    {
//        return "None";
//    }
//
//    return pwd;
 
    const char* pwd = getcwd(cwd,sizeof(cwd));
    if(pwd == NULL)
    {
        return "None";
    }
    return pwd;
}


const char* GetHostName()
{
    const char* HostName = getenv("HOSTNAME");
    if(HostName == NULL)
    {
        return "None";
    }

    return HostName;
}

const char* GetHome()
{
    std:: string home = getenv("HOME");
    return home.empty()? "" : home.c_str();
}

std::string StripPath()
{
    std::string st = GetPwd();
    if(st == SPARATOR)
    {
        st = "~";
    }

    size_t pos = st.rfind("/");
    return  st.substr(pos+1);
}

void IntiEnv()
{
    memset(env,0,sizeof(env));
    extern char** environ;
    for(int i =0;environ[i];i++)
    {
        env[i] = (char*)malloc(strlen(environ[i])+1);
        strcpy(env[i],environ[i]);
        envs++;
    }

    env[envs++] = (char*)"HHHHHHHHH=123456";
    env[envs] = NULL;
    for(int i=0; i < envs ; i++)
    {
        putenv(env[i]);
    }

}

void MakeCommandLine(char commandline[], int size)
{
   snprintf(commandline ,size , FORMAT , GetUserName() , GetHostName() , StripPath().c_str());
   printf("%s",commandline);
}

//这一步是得到“ls -a -l\n” ->"ls -a -l"
bool GetCommandLine(char* command)
{
     char* p = fgets(command,COMMANDMAXSIZE,stdin);
     if(p == NULL)
     {
         return false;
     }
     int len = strlen(command);
     command[len-1] = 0;
     return true;
}

//这一步是把“ls -a -l”->"ls" "-a" "-l"
bool CommandPhrase(char command[])
{
    size=0;
    argv[size++] = strtok(command , " ");
    while(argv[size++] = strtok(NULL," "));
    size--;
    return true;
}
bool  CreatChildProcess()
{
    
   pid_t pid =  fork();
   if(pid == 0)
   {
      // printf("flag = %d\n",flag);
        if(flag != NOREDIR)
        {
            if(flag == OUTPUTREDIR)
            {
                 int fd = open(filename.c_str(),O_CREAT|O_WRONLY|O_TRUNC,0666);
                 if(fd <0)
                 {
                     exit(1);
                 }
                 dup2(fd,1);
                 close(fd);
            }
            else if(flag == APPENDREDIR)
            {

                 int fd = open(filename.c_str(),O_CREAT|O_WRONLY|O_APPEND);
                 if(fd < 0)
                 {
                     exit(2);
                 }
                 dup2(fd,1);
                 close(fd);
            }
            else
            {
                int fd =open(filename.c_str(),O_RDONLY);
                if(fd < 0)
                {
                    exit(3);
                }
                dup2(fd,0);
                close(fd);
            }
        }
        //child process
        execvp(argv[0],argv);
        exit(1);
   }
  pid_t ret =  waitpid(pid , NULL , 0);
  if(ret < 0)
  {
    printf("WRONG!\n");
    return false;
  }
  return true;
}

bool Cd()
{
    if(size == 1)
    {
        std::string st = GetHome();
        chdir(st.c_str());
    }
    else
    {
      chdir(argv[1]);
    }
      return true;
}

bool InternalKeyCommand()
{
    std::string st = argv[0]; 

    if( st == "cd")
    {
        Cd();
        return true;
    }
    return false;
}
void DeleteSpace(char cmd[] ,int& size)
{
    size++;
    while(true)
    {
        if(cmd[size] == ' ')
        {
            cmd[size++] = '\0';
        }
        else
        {
            break;
        }
    }
}

void RedirCheck(char  command [])
{
    flag = NOREDIR;
    filename.clear();
    int size = strlen(command);
    while(size)
    {
        if(command[size] == '<')//输入重定向
        {
            //std::cout<<"进入到输入重定向"<<std::endl;
            command[size] = 0;
            DeleteSpace(command , size);
            filename = command +size;
            flag = INPUTREDIR;
            break;
        }
        else if(command[size] == '>')
        {
            if(command[size-1] == '>')//追加重定向
            {
                //std::cout<<"进入到追加重定向"<<std::endl;
                command[size] = 0;
                command[size-1] = 0;
                DeleteSpace(command , size);
                filename = command +size;
                flag = APPENDREDIR;

                //std::cout<<flag<<std::endl;
                break;
            }
            else//输出重定向
            {
                //std::cout<<"进入到输出重定向"<<std::endl;
                command[size] = 0;
                DeleteSpace(command,size);
                filename = command + size;
                flag = OUTPUTREDIR;
                break;
            }
        }
        else
        {
            size--;
        }

    }
}

void test()
{
    // char command [1024];
    // CommandPhase(command);
    // printf("%s\n", command);
    int num = size;
    while(num--)
    {
        printf("%s\n",argv[num]);
    }
    
}

int main()
{
    IntiEnv();
    while(true)
    {
    //1.创建命令行
        char line[COMMANDMAXSIZE];
        MakeCommandLine(line,COMMANDMAXSIZE);
    //2."ls -a -l\n"->"ls -a -l"
        char command[COMMANDMAXSIZE];
       if(!GetCommandLine(command))
          continue ;


    //4.判断是否是重定向操作
        RedirCheck(command);
       // printf("%s \n",command);
       // printf(" %s / %s", command , filename.c_str());
       // RedirExecute();
   //std::cout<<"将要进入子进程"<<flag<<std::endl;
    //3."ls -a -l"->"ls" "-a" "-l"
        CommandPhrase(command);
    //4.内建命令由父进程自己执行
      // test();
      // if(InternalKeyCommand())
      //     continue;
   //std::cout<<"将要进入子进程"<<flag<<std::endl;
       if(InternalKeyCommand())
           continue;
    //5.创建子进程，用execvp进程替换
       // test();
   //std::cout<<"将要进入子进程"<<flag<<std::endl;
       if(!CreatChildProcess())
          continue;
    }
    return 0; 
}
